/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

#include "DXUT.h"
#include "BuddhabrotCPU.h"
#include "DXHelper.h"
#include "DataBufferDX11.h"
#include "RandomMwc.h"

/**
*/
BuddhabrotCPU::BuddhabrotCPU()
{
}

/**
*/
BuddhabrotCPU::~BuddhabrotCPU()
{
}

/**
*/
void BuddhabrotCPU::Create( DataBufferDX11* pDataBuffer )
{
    int nDataCount = pDataBuffer->GetCount();
    m_iterations.clear();
    m_iterations.resize( nDataCount );
}

/**
    Iterate and determine whether a complex number is a member of the mandelbrot set.
    @param zRe    [in]        the real part of a complex number
    @param zIm    [in]        the imaginary part of a complex number
*/
int BuddhabrotCPU::Iterate( float zRe, float zIm ) 
{
    float cRe = zRe;
    float cIm = zIm;

    for( int nIteration=0; nIteration<m_cb.nMaxIteration; ++nIteration )
    {
        float zRe2 = zRe*zRe;
        float zIm2 = zIm*zIm;

        if( zRe2 + zIm2 > 4.0f ) {
            return nIteration;
        }

        // calculate next z(n)
        zIm = 2*zRe*zIm + cIm;
        zRe = zRe2 - zIm2 + cRe;
    }

    // complex number did not escape to infinity 
    return m_cb.nMaxIteration;
}

/**
    Iterate and plot the path of the escaping complex number.
    @param zRe                [in]        the real part of a complex number
    @param zIm                [in]        the imaginary part of a complex number
    @param nWidth            [in]        width of the output buffer
    @param nHeight            [in]        height of the output buffer
    @param nIterationEscape    [in]        the maximum iterations to plot
*/
void BuddhabrotCPU::IterateAndPlot( float zRe, float zIm, int nWidth, int nHeight, int nIterationEscape ) 
{
    float cRe = zRe;
    float cIm = zIm;

    for( int nIteration=0; nIteration<=nIterationEscape; ++nIteration )
    {
        //int nX = static_cast< int >( nWidth * (zRe - m_cb.fMinReZoom) / ( (m_cb.fMaxReZoom-m_cb.fMinReZoom) ) );
        //int nY = static_cast< int >( nHeight * (zIm - m_cb.fMinImZoom) / ( (m_cb.fMaxImZoom-m_cb.fMinImZoom) ) );
        int nX = static_cast< int >( (zRe - m_cb.fMinReZoom) * m_cb.fReC );
        int nY = static_cast< int >( (zIm - m_cb.fMinImZoom) * m_cb.fImC );
        
        if( nX >=0 && nY >=0 && nX < nWidth && nY < nHeight ) 
        {
            int nIndex = nX + nWidth*nY;
            
            // red
            if( nIteration < m_cb.maxIterations[ 0 ] ) {
                ++m_iterations[ nIndex ].d[ 0 ];
            }
            // green
            if( nIteration < m_cb.maxIterations[ 1 ] ) {
                ++m_iterations[ nIndex ].d[ 1 ];
            }
            // blue
            if( nIteration < m_cb.maxIterations[ 2 ] ) {
                ++m_iterations[ nIndex ].d[ 2 ];
            }
        }

        float zRe2 = zRe*zRe;
        float zIm2 = zIm*zIm;

        // calculate next z(n)
        zIm = 2*zRe*zIm + cIm;
        zRe = zRe2 - zIm2 + cRe;
    }
}

/**
*/
void BuddhabrotCPU::Render( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext, DataBufferDX11* pDataBuffer )
{    
    const unsigned int nWidth = static_cast< unsigned int >( pDataBuffer->GetWidth() );
    const unsigned int nHeight = static_cast< unsigned int >( pDataBuffer->GetHeight() );
    BufType* buffer = pDataBuffer->GetpData();

    UpdateConstantBuf( nWidth, nHeight, nWidth, nHeight );

    // 10000 samples per frame (needs to be the same as compute shader)
    for( unsigned int i=0; i<BUDDHABROT_SAMPLES_PER_FRAME; ++i )
    {
        float cRe = g_randomMwc.getFloatInRange( m_cb.fMinRe, m_cb.fMaxRe );
        float cIm = g_randomMwc.getFloatInRange( m_cb.fMinIm, m_cb.fMaxIm );
        
        float zRe = cRe;
        float zIm = cIm;

        BufType color = BufType( 0, 0, 0, 1 );

        // check whether sample complex number is in the mandelbrot set.
        int nIterationEscape = Iterate( zRe, zIm );

        if( nIterationEscape != m_cb.nMaxIteration ) { 
            // If the complex number is not a member of the mandelbrot set, plot the complex number's path as
            // it escapes to infinity. 
            IterateAndPlot( zRe, zIm, nWidth, nHeight, nIterationEscape );
        }    
    }

    // copy iteration buffer results to the DataBufferDX11 data buffer array. 
    for( size_t i=0; i<m_iterations.size(); ++i ) {
        buffer[ i ] = BufType( m_iterations[ i ].d[ 0 ], m_iterations[ i ].d[ 1 ], m_iterations[ i ].d[ 2 ], 1 );
    }
    
    // copy from cpu to gpu
    pDataBuffer->UpdateBuffer( pd3dDevice, pd3dImmediateContext );
}

void BuddhabrotCPU::ClearIterations()
{
    // set to 0
    for( size_t i=0; i<m_iterations.size(); ++i ) {
        m_iterations[ i ] = BufType();
    }
}

/**
*/
void BuddhabrotCPU::Destroy()
{
}